import {
  createDirectory,
  loadConfigurationFile as loadRawConfigurationFile,
  safeRemove,
  saveConfigurationFile
} from '@platformatic/foundation'
import { deepStrictEqual, ok } from 'node:assert'
import { readFile } from 'node:fs/promises'
import { relative, resolve, sep } from 'node:path'
import { test } from 'node:test'
import { prepareRuntime, temporaryFolder } from '../../basic/test/helper.js'
import { appendEnvVariable } from '../lib/commands/external.js'
import { changeWorkingDirectory, prepareGitRepository, wattpmUtils } from './helper.js'

test('resolve - should clone a URL when the environment variable is set to a folder inside the repo', async t => {
  const { root: rootDir } = await prepareRuntime(t, 'main', false, 'watt.json')
  const repo = await prepareGitRepository(t, rootDir)
  t.after(() => safeRemove(rootDir))

  changeWorkingDirectory(t, rootDir)
  await wattpmUtils('import', rootDir, '-H', '-i', 'resolved', '{PLT_GIT_REPO_URL}')
  await appendEnvVariable(resolve(rootDir, '.env'), 'PLT_APPLICATION_RESOLVED_PATH', 'web/resolved')

  const resolveProcess = await wattpmUtils('resolve', rootDir)

  ok(resolveProcess.stdout.includes(`Cloning ${repo} into web${sep}resolved`))
  ok(resolveProcess.stdout.includes('Installing dependencies for the application resolved using npm ...'))

  deepStrictEqual(await readFile(resolve(rootDir, 'web/resolved/branch'), 'utf-8'), 'main')
})

test('resolve - should clone a URL when the environment variable is not set', async t => {
  const { root: rootDir } = await prepareRuntime(t, 'main', false, 'watt.json')
  const repo = await prepareGitRepository(t, rootDir)
  t.after(() => safeRemove(rootDir))

  changeWorkingDirectory(t, rootDir)
  await wattpmUtils('import', rootDir, '-H', '-i', 'resolved', '{PLT_GIT_REPO_URL}')

  const resolveProcess = await wattpmUtils('resolve', rootDir)

  ok(resolveProcess.stdout.includes(`Cloning ${repo} into ${relative(rootDir, 'external/resolved')}`))
  ok(resolveProcess.stdout.includes('Installing dependencies for the application resolved using npm ...'))
})

test('resolve - should do nothing when the directory already exists inside the repo', async t => {
  const { root: rootDir } = await prepareRuntime(t, 'main', false, 'watt.json')
  const repo = await prepareGitRepository(t, rootDir)
  t.after(() => safeRemove(rootDir))

  changeWorkingDirectory(t, rootDir)
  await wattpmUtils('import', rootDir, '-H', '-i', 'resolved', '{PLT_GIT_REPO_URL}')

  const envValue = resolve(rootDir, 'whatever')
  await createDirectory(envValue)
  await appendEnvVariable(resolve(rootDir, '.env'), 'PLT_APPLICATION_RESOLVED_PATH', 'whatever')

  const resolveProcess = await wattpmUtils('resolve', rootDir)
  ok(!resolveProcess.stdout.includes(`Cloning ${repo}`))
  ok(!resolveProcess.stdout.includes('Installing dependencies for the application resolved using npm ...'))
})

test('resolve - should do nothing when loaded vian application file', async t => {
  const { root: rootDir } = await prepareRuntime(t, 'main', false, 'watt.json')
  const repo = await prepareGitRepository(t, rootDir)
  t.after(() => safeRemove(rootDir))

  await safeRemove(resolve(rootDir, 'watt.json'))
  await saveConfigurationFile(resolve(rootDir, 'web/main/watt.json'), {
    $schema: 'https://schemas.platformatic.dev/@platformatic/node/2.3.1.json'
  })

  changeWorkingDirectory(t, resolve(rootDir, 'web/main'))
  const resolveProcess = await wattpmUtils('resolve', resolve(rootDir, 'web/main'))
  ok(!resolveProcess.stdout.includes(`Cloning ${repo}`))
  ok(!resolveProcess.stdout.includes('Installing dependencies for the application resolved using npm ...'))
})

test('resolve - should do nothing when the directory already exists outside the repo', async t => {
  const { root: rootDir } = await prepareRuntime(t, 'main', false, 'watt.json')
  const repo = await prepareGitRepository(t, rootDir)
  t.after(() => safeRemove(rootDir))

  changeWorkingDirectory(t, rootDir)
  await wattpmUtils('import', rootDir, '-H', '-i', 'resolved', '{PLT_GIT_REPO_URL}')

  const envValue = resolve(temporaryFolder, 'outside-' + Date.now())
  await createDirectory(envValue)
  await appendEnvVariable(resolve(rootDir, '.env'), 'PLT_APPLICATION_RESOLVED_PATH', envValue)

  const resolveProcess = await wattpmUtils('resolve', rootDir)
  ok(!resolveProcess.stdout.includes(`Cloning ${repo}`))
  ok(!resolveProcess.stdout.includes('Installing dependencies for the application resolved using npm ...'))
})

test('resolve - should do nothing when the autogenerated directory already exists inside the repo', async t => {
  const { root: rootDir } = await prepareRuntime(t, 'main', false, 'watt.json')
  const repo = await prepareGitRepository(t, rootDir)
  t.after(() => safeRemove(rootDir))

  changeWorkingDirectory(t, rootDir)
  await wattpmUtils('import', rootDir, '-H', '-i', 'resolved', '{PLT_GIT_REPO_URL}')
  await createDirectory(resolve(rootDir, 'external/resolved'))

  const resolveProcess = await wattpmUtils('resolve', rootDir)
  ok(!resolveProcess.stdout.includes(`Cloning ${repo}`))
  ok(!resolveProcess.stdout.includes('Installing dependencies for the application resolved using npm ...'))
  ok(
    resolveProcess.stdout.includes(
      `Skipping application resolved as the generated path external${sep}resolved already exists.`
    ),
    resolveProcess.stdout
  )
})

test('resolve - should throw an error when the directory outside the repo do not exist', async t => {
  const { root: rootDir } = await prepareRuntime(t, 'main', false, 'watt.json')
  const repo = await prepareGitRepository(t, rootDir)
  t.after(() => safeRemove(rootDir))

  changeWorkingDirectory(t, rootDir)
  await wattpmUtils('import', rootDir, '-H', '-i', 'resolved', '{PLT_GIT_REPO_URL}')

  const envValue = resolve(temporaryFolder, 'outside-' + Date.now())
  await appendEnvVariable(resolve(rootDir, '.env'), 'PLT_APPLICATION_RESOLVED_PATH', envValue)

  const resolveProcess = await wattpmUtils('resolve', rootDir, { reject: false })
  ok(!resolveProcess.stdout.includes(`Cloning ${repo}`))
  ok(!resolveProcess.stdout.includes('Installing dependencies for the application resolved using npm ...'))
  ok(
    resolveProcess.stdout.includes(
      `Skipping application resolved as the non existent directory ${envValue} is outside the project directory.`
    )
  )
})

test('resolve - should attempt to clone with username and password', async t => {
  const { root: rootDir } = await prepareRuntime(t, 'main', false, 'watt.json')
  t.after(() => safeRemove(rootDir))

  const url = 'https://127.0.0.1:60000/platformatic/wattpm-fixtures.git'
  changeWorkingDirectory(t, rootDir)
  await wattpmUtils('import', rootDir, '-i', 'resolved', url)
  const resolveProcess = await wattpmUtils('resolve', '-u', 'foo', '-p', 'bar', rootDir, { reject: false })

  ok(resolveProcess.stdout.includes(`Cloning ${url} as user foo`))
  ok(resolveProcess.stdout.includes(`Cloning into '${resolve(rootDir, 'external/resolved')}'`))
  ok(resolveProcess.stdout.includes('Unable to clone repository of the application resolved'))
})

test('resolve - should clone a different branch', async t => {
  const { root: rootDir } = await prepareRuntime(t, 'main', false, 'watt.json')
  const repo = await prepareGitRepository(t, rootDir)
  t.after(() => safeRemove(rootDir))

  changeWorkingDirectory(t, rootDir)
  await wattpmUtils('import', rootDir, '-H', '-i', 'resolved', '-b', 'another', '{PLT_GIT_REPO_URL}')
  await appendEnvVariable(resolve(rootDir, '.env'), 'PLT_APPLICATION_RESOLVED_PATH', 'web/resolved')

  const resolveProcess = await wattpmUtils('resolve', rootDir)

  ok(resolveProcess.stdout.includes(`Cloning ${repo} (branch another) into web${sep}resolved`))
  ok(resolveProcess.stdout.includes('Installing dependencies for the application resolved using npm ...'))

  deepStrictEqual(await readFile(resolve(rootDir, 'web/resolved/branch'), 'utf-8'), 'another')
})

test('resolve - should install dependencies using a different package manager', async t => {
  const { root: rootDir } = await prepareRuntime(t, 'main', false, 'watt.json')
  const repo = await prepareGitRepository(t, rootDir)
  t.after(() => safeRemove(rootDir))

  changeWorkingDirectory(t, rootDir)
  await wattpmUtils('import', rootDir, '-H', '-i', 'resolved', '{PLT_GIT_REPO_URL}')
  await appendEnvVariable(resolve(rootDir, '.env'), 'PLT_APPLICATION_RESOLVED_PATH', 'web/resolved')

  const resolveProcess = await wattpmUtils('resolve', '-P', 'pnpm', rootDir)

  ok(resolveProcess.stdout.includes(`Cloning ${repo} into web${sep}resolved`))
  ok(resolveProcess.stdout.includes('Installing dependencies for the project using pnpm ...'))
  ok(resolveProcess.stdout.includes('Installing dependencies for the application resolved using pnpm ...'))

  deepStrictEqual(await readFile(resolve(rootDir, 'web/resolved/branch'), 'utf-8'), 'main')
})

test('install - should respect the application package manager, if any', async t => {
  const { root: rootDir } = await prepareRuntime(t, 'main', false, 'watt.json')
  await prepareGitRepository(t, rootDir)
  t.after(() => safeRemove(rootDir))

  changeWorkingDirectory(t, rootDir)
  await wattpmUtils('import', rootDir, '-H', '-i', 'resolved', '{PLT_GIT_REPO_URL}')
  await appendEnvVariable(resolve(rootDir, '.env'), 'PLT_APPLICATION_RESOLVED_PATH', 'web/resolved')

  const configurationFile = resolve(rootDir, 'watt.json')
  const originalFileContents = await loadRawConfigurationFile(configurationFile)
  originalFileContents.web[0].packageManager = 'npm'
  await saveConfigurationFile(configurationFile, originalFileContents)

  const resolveProcess = await wattpmUtils('resolve', '-P', 'pnpm', rootDir)

  ok(resolveProcess.stdout.includes('Installing dependencies for the application resolved using npm ...'))
})

test('resolve - should parse branch from git URL fragment', async t => {
  const { root: rootDir } = await prepareRuntime(t, 'main', false, 'watt.json')
  const repo = await prepareGitRepository(t, rootDir)
  t.after(() => safeRemove(rootDir))

  changeWorkingDirectory(t, rootDir)

  // Use git URL with #branch fragment
  const gitUrlWithBranch = `${repo}#another`
  await wattpmUtils('import', rootDir, '-H', '-i', 'resolved', gitUrlWithBranch)
  await appendEnvVariable(resolve(rootDir, '.env'), 'PLT_APPLICATION_RESOLVED_PATH', 'web/resolved')

  const resolveProcess = await wattpmUtils('resolve', rootDir)

  ok(resolveProcess.stdout.includes(`Cloning ${repo}`))
  ok(resolveProcess.stdout.includes('Installing dependencies for the application resolved using npm ...'))

  // Verify it cloned the 'another' branch, not 'main'
  deepStrictEqual(await readFile(resolve(rootDir, 'web/resolved/branch'), 'utf-8'), 'another')
})

test('resolve - branch flag should take precedence over URL fragment', async t => {
  const { root: rootDir } = await prepareRuntime(t, 'main', false, 'watt.json')
  const repo = await prepareGitRepository(t, rootDir)
  t.after(() => safeRemove(rootDir))

  changeWorkingDirectory(t, rootDir)

  // Use git URL with #main fragment, but specify -b another
  const gitUrlWithBranch = `${repo}#main`
  await wattpmUtils('import', rootDir, '-H', '-i', 'resolved', '-b', 'another', gitUrlWithBranch)
  await appendEnvVariable(resolve(rootDir, '.env'), 'PLT_APPLICATION_RESOLVED_PATH', 'web/resolved')

  const resolveProcess = await wattpmUtils('resolve', rootDir)

  ok(resolveProcess.stdout.includes(`Cloning ${repo}`))
  ok(resolveProcess.stdout.includes('branch another'))

  // Verify it cloned 'another' branch (from -b flag), not 'main' (from fragment)
  deepStrictEqual(await readFile(resolve(rootDir, 'web/resolved/branch'), 'utf-8'), 'another')
})
